Skip to content

Upgrade springboot-java8 to Java 17 LTS - #74

Open
tobydrinkall wants to merge 2 commits into
masterfrom
java-17-upgrade-v2
Open

tobydrinkall wants to merge 2 commits into
masterfrom
java-17-upgrade-v2

Conversation

@tobydrinkall

@tobydrinkall tobydrinkall commented Aug 7, 2026

Copy link
Copy Markdown

Summary

Moves both build paths (Maven and Gradle) to JDK 17 with --release 17 and Spring Boot 2.7.18, the last Java 17-compatible 2.x line. The javax.* namespace is retained deliberately — no jakarta.* migration.

Two non-obvious changes:

  • pom.xml had <packaging>pom</packaging>, which silently prevented spring-boot-maven-plugin from producing an executable jar. Now jar, so target/gs-spring-boot-0.1.0.jar is runnable.
  • Application fetched a quote from the long-dead gturnquist-quoters.cfapps.io in main and in the CommandLineRunner; the resulting RestClientException aborted startup on any JDK. Both call sites now go through one helper that survives both a failed call and an empty body (getForObject returns null on 204/empty rather than throwing):
private static void logRandomQuote(RestTemplate restTemplate) {
    try {
        Quote quote = restTemplate.getForObject(QUOTERS_URL, Quote.class);
        if (quote == null) log.warn("Unable to retrieve random quote from {}", QUOTERS_URL);
        else log.info(quote.toString());
    } catch (RestClientException ex) { log.warn("Unable to retrieve random quote from {}", QUOTERS_URL, ex); }
}

Build / dependency changes

  • pom.xml: parent 2.0.2.RELEASE2.7.18; packaging pomjar; java.version 1.817; added maven.compiler.release=17, explicit maven-compiler-plugin 3.13.0 with <release>17</release>, maven-surefire-plugin.version=3.5.6, javax.annotation:javax.annotation-api:1.3.2; removed spring-boot-properties-migrator.
  • build.gradle: Boot Gradle plugin → 2.7.18, added io.spring.gradle:dependency-management-plugin:1.0.15.RELEASE; sourceCompatibility/targetCompatibility 1.8 → Java 17 toolchain + options.release = 17; bootJar.baseName/versionarchiveBaseName/archiveVersion (the legacy properties are removed in Gradle 8); compile/testCompileimplementation/testImplementation.
  • Wrappers: Maven 3.3.93.9.6, Gradle 4.67.6.4; mvnw/gradlew made executable so CI can invoke them.

CI

New .github/workflows/build.yml with two JDK 17 (temurin) jobs: mvn -B -V clean package and ./gradlew -V clean bootJar. Both are green on this PR.

Verification

No tests exist in this repo, so verification is compile + package + runtime smoke test.

Apache Maven 3.9.6 / Java version: 17.0.13
[INFO] Compiling 13 source files with javac [debug parameters release 17]
[INFO] --- surefire:3.5.6:test (default-test) @ gs-spring-boot --- No tests to run.
[INFO] BUILD SUCCESS

Gradle 7.6.4 / JVM: 17.0.13
> Task :bootJar
BUILD SUCCESSFUL

java -jar target/gs-spring-boot-0.1.0.jar:

INFO  Started Application in 1.077 seconds
INFO  Creating tables
WARN  Unable to retrieve random quote from http://gturnquist-quoters.cfapps.io/api/random

The WARN appears twice (once per call site) and startup completes. H2 2.1.214 (managed by Boot 2.7.18) accepts the existing inline SQL (DROP TABLE customers IF EXISTS, SERIAL).

GET /topic200:

[{"id":"spring","subjectName":"Spring Framework","subjectDescription":"Spring Framework Description"},{"id":"java","subjectName":"Core Java","subjectDescription":"Java Description"},{"id":"javascript","subjectName":"javascript Framework","subjectDescription":"javascript Framework Description"}]

Deviations from the plan

  • Branch is java-17-upgrade-v2: a superseded java-17-upgrade branch already existed on the remote and was left untouched rather than force-pushed.
  • build.gradle was missing spring-boot-starter-jdbc and h2, which the existing sources require; added so the Gradle build compiles and the jar runs (the Maven build already had them).
  • javax.annotation-api:1.3.2 was added to pom.xml as well as build.gradle to pin it in both builds.

Known pre-existing issue (not fixed — out of upgrade scope)

TopicService.getTopicWithId calls Optional.get() without a presence check, so GET /topic/{unknownId} returns HTTP 500. This exists on master and is unrelated to the upgrade.

Link to Devin session: https://app.devin.ai/sessions/73dca597bcd744a4aa1459f40d98f5c1
Requested by: @tobydrinkall


Devin Review

Status Commit
⚪ Not started

Run Devin Review

Open in Devin Review (Staging)
Open in Devin Review

Co-Authored-By: Toby Drinkall <toby.drinkall@cognition.ai>
@tobydrinkall tobydrinkall self-assigned this Aug 7, 2026
@devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 6 potential issues.

Open in Devin Review

Comment thread src/main/java/hello/Application.java
Comment thread build.gradle
Comment thread pom.xml
Comment on lines 27 to 29
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Maven and Gradle builds differ in H2 dependency scope

pom.xml declares com.h2database:h2 in default (compile) scope while build.gradle:40 uses runtimeOnly. No source references H2 classes directly, so both work, but keeping the two build files aligned (e.g. <scope>runtime</scope> in Maven) avoids divergence between the packaged artifacts.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving Maven at compile scope for now — this PR is scoped to the JDK 17 / Boot 2.7.18 upgrade and both builds package H2 identically today. Worth aligning in a follow-up.

Comment thread pom.xml
<java.version>17</java.version>
<maven.compiler.release>17</maven.compiler.release>
<maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.5.6</maven-surefire-plugin.version>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 surefire version property is declared but no plugin pins it

maven-surefire-plugin.version is set to 3.5.6, which overrides the Spring Boot parent's managed surefire version. Worth confirming that surefire 3.5.6 is actually released and compatible with the pinned parent, since no <plugin> entry in the build makes the override explicit and a bad value would only surface at test time (this project currently has no tests).

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: surefire 3.5.6 resolves and runs under the 2.7.18 parent — the verified build logs --- surefire:3.5.6:test (default-test) @ gs-spring-boot ---. The pin is intentional (a standing version decision for this upgrade), so no explicit <plugin> entry needed.

Comment on lines +14 to +23
gradle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- uses: gradle/actions/setup-gradle@v3
- run: ./gradlew -V clean bootJar

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Gradle CI job skips dependency caching that the Maven job enables

The maven job sets cache: maven on setup-java, but the gradle job relies solely on gradle/actions/setup-gradle@v3. That action does provide Gradle caching by default, so this is not broken, but the asymmetry (no cache: gradle on setup-java) is worth noting if build time matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional: gradle/actions/setup-gradle@v3 already caches Gradle dependencies, so adding cache: gradle to setup-java would be redundant.

Comment on lines 38 to 40
}

RestTemplate restTemplate = new RestTemplate();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Quote is still fetched twice on startup

main calls logRandomQuote after SpringApplication.run has already executed the CommandLineRunner bean that does the same fetch, so the (now warning-only) remote call happens twice per startup. The description acknowledges the duplicated WARN; removing the main-side call and the ad-hoc new RestTemplate() would avoid an unnecessary outbound request on every boot.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional — calling the quote fetch from both main and the CommandLineRunner is an explicit requirement of this upgrade, preserving the original program's behaviour. Now that it only WARNs, the duplicate outbound call is harmless.

Co-Authored-By: Toby Drinkall <toby.drinkall@cognition.ai>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

Open in Devin Review

Comment thread pom.xml
Comment on lines +30 to +34
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Unused javax.annotation-api dependency pinned in both builds

No source file under src/main/java references javax.annotation (verified by grep). Since Spring Boot 2.7 still targets Java EE javax.* and the JDK 17 removal of java.xml.ws.annotation only matters when the code actually uses @Resource/@PostConstruct, this explicit 1.3.2 pin is dead weight and an unmanaged version that will drift from Boot's BOM.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The javax.annotation-api:1.3.2 pin is an explicit requirement of this upgrade (a standing decision to keep the javax.* namespace explicitly available rather than implicitly), so it stays even though no source references it today.

distribution: temurin
java-version: '17'
- uses: gradle/actions/setup-gradle@v3
- run: ./gradlew -V clean bootJar

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Gradle CI job only builds bootJar, so test compilation is never exercised

The Maven job runs clean package (compiles and runs tests), but the Gradle job runs only clean bootJar, which never resolves testCompileClasspath or compiles test sources. Any future test-scope dependency problem (e.g. testImplementation("junit:junit") losing BOM-managed versioning) would go undetected in CI. Using ./gradlew build would keep the two jobs symmetric.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow content is specified for this upgrade, and the repo has no test sources at all (surefire logs No tests to run), so bootJar vs build makes no practical difference here. Reasonable follow-up once tests exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant